package com.gzist.elderlyserviceplatform.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gzist.elderlyserviceplatform.common.Result;
import com.gzist.elderlyserviceplatform.entity.dto.*;
import com.gzist.elderlyserviceplatform.entity.po.User;
import com.gzist.elderlyserviceplatform.interceptors.exceptions.FormValidationException;
import com.gzist.elderlyserviceplatform.interceptors.exceptions.LoginFailedException;
import com.gzist.elderlyserviceplatform.interceptors.exceptions.RegisterFailedException;
import com.gzist.elderlyserviceplatform.mapper.UserMapper;
import com.gzist.elderlyserviceplatform.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import net.sf.jsqlparser.util.validation.metadata.NamedObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static net.sf.jsqlparser.util.validation.metadata.NamedObject.user;

/**
 * 用户表 服务实现类
 */
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final StringRedisTemplate redisTemplate;

    /**
     * 用户登录方法
     *
     * @param loginDTO 包含用户登录信息的DTO对象，包括登录账号和密码
     * @throws LoginFailedException 当登录账号或密码验证失败时抛出
     */
    @Override
    public void login(LoginDTO loginDTO) {
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        if (StrUtil.isNotBlank(loginDTO.getUsername())) {
            wrapper.eq(User::getUsername, loginDTO.getUsername());
        }
        User user = this.getOne(wrapper);
        if (user == null) {
            throw new LoginFailedException("账号错误！");
        }
        if (!StrUtil.equals(user.getPassword(), loginDTO.getPassword())) {
            throw new LoginFailedException("密码错误！");
        }
        if (!StrUtil.equals(user.getRole(), loginDTO.getRole())) {
            throw new LoginFailedException("此角色不存在该用户！");
        }
        StpUtil.login(user.getId(), loginDTO.isRemember());
        StpUtil.getSession().set("user", user);
    }


    /**
     * 用户注册方法
     *
     * @param registerUserDto 包含用户注册信息的DTO对象，包括登录账号和密码、重复密码
     * @throws RuntimeException 当登录账号或密码验证失败时抛出
     */
    public void register(RegisterUserDto registerUserDto) {
        //判断账号，密码是否为空
        if (registerUserDto.getUsername().isEmpty()) {
            throw new RegisterFailedException("输入账号为空");
        }
        if (registerUserDto.getPassword().isEmpty()) {
            throw new RegisterFailedException("输入密码为空");
        }
        //判断密码是否相同
        if (!registerUserDto.getPassword().equals(registerUserDto.getRepeatedPassword())) {
            throw new RegisterFailedException("两次输入的密码不相同");
        }
        //是否存在重复用户
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        if (StrUtil.isNotBlank(registerUserDto.getUsername())) {
            wrapper.eq(User::getUsername, registerUserDto.getUsername());
        }
        User user = this.getOne(wrapper);
        //存在抛出异常
        if (user != null) {
            throw new RegisterFailedException("用户已存在");
        }
        //拷贝属性给用户类,添加用户
        User user1 = new User();
        BeanUtil.copyProperties(registerUserDto, user1);
        //注册用户
        this.save(user1);
    }

    @Override
    public Page<User> queryPage(UserPageDTO userPageDTO) {
        Page<User> page = new Page<>(userPageDTO.getPageNum(), userPageDTO.getPageSize());
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery();
        // 添加删除标志过滤条件
        wrapper.eq(User::getDelFlag, 0);
        // 只有当 role 不为空时才添加角色过滤条件
        if (StrUtil.isNotBlank(userPageDTO.getRole())) {
            wrapper.eq(User::getRole, userPageDTO.getRole());
        }
        // 只有当 username 不为空时才添加用户名模糊查询条件
        if (StrUtil.isNotBlank(userPageDTO.getUsername())) {
            wrapper.like(User::getUsername, userPageDTO.getUsername());
        }
        // 按创建时间降序排序
        wrapper.orderByDesc(User::getCreateTime);
        return this.page(page, wrapper);
    }

    @Override
    public void updateUser(UserInfoSave0rUpdateDTO user) {
        if (StrUtil.isBlank(user.getId())) {
            throw new FormValidationException("用户id不能为空");
        }
        if (StrUtil.isBlank(user.getUsername())) {
            throw new FormValidationException("用户账号不能为空");
        }
        LambdaUpdateWrapper<User> wrapper = Wrappers.lambdaUpdate();
        wrapper.eq(User::getId, user.getId()).set(User::getUsername, user.getUsername()).set(User::getName, user.getName()).set(User::getPhone, user.getPhone()).set(User::getEmail, user.getEmail()).set(User::getAvatar, user.getAvatar()).set(User::getUpdateTime, LocalDateTime.now());
        this.update(wrapper);
    }

    @Override
    public String verifyAccount(Map<String, String> param) {
        String username = param.get("username");
        if (StrUtil.isBlank(username)) {
            throw new FormValidationException("账号不能为空");
        }
        User user = this.lambdaQuery().eq(User::getDelFlag, 0).eq(User::getUsername, username).one();

        if (user == null) {
            throw new FormValidationException("账号不存在");
        }
        String verifyToken = IdUtil.fastUUID();
        // 存储令牌与用户名的映射关系
        redisTemplate.opsForValue().set("pwd_reset:" + verifyToken, username, 10, TimeUnit.MINUTES);
        return verifyToken;
    }

    @Override
    @Transactional
    public void resetPassword(Map<String, String> param) {
        String username = param.get("username");
        String verifyToken = param.get("verifyToken");
        String newPassword = param.get("newPassword");
        // 参数校验增强
        if (StrUtil.hasBlank(username, verifyToken, newPassword)) {
            throw new FormValidationException("参数不完整");
        }
        // 获取Redis存储的用户名
        String redisKey = "pwd_reset:" + verifyToken;
        String verifyUsername = redisTemplate.opsForValue().get(redisKey);
        // 令牌有效性校验
        if (StrUtil.isBlank(verifyUsername)) {
            throw new FormValidationException("验证令牌已失效");
        }
        // 关键安全校验
        if (!username.equals(verifyUsername)) {
            throw new FormValidationException("用户身份不匹配");
        }
        User user = this.lambdaQuery().eq(User::getUsername, username).eq(User::getDelFlag, 0).one();
        if (user == null) {
            throw new FormValidationException("账号不存在");
        }
        user.setPassword(newPassword);
        // 原子操作：更新密码并删除令牌
        redisTemplate.delete(redisKey);
        this.updateById(user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePassword(UpdatePasswordDTO updatePasswordDTO) {
        // 参数基础校验
        validateParams(updatePasswordDTO);
        // 获取当前管理员
        User user = (User) StpUtil.getSession().get("user");
        // 验证原密码
        if (!StrUtil.equals(updatePasswordDTO.getOldPassword(), user.getPassword())) {
            throw new FormValidationException("原密码验证失败");
        }
        // 验证密码一致性
        validatePasswordConsistency(updatePasswordDTO.getNewPassword(), updatePasswordDTO.getConfirmPassword());
        // 更新密码并退出登录
        updateAdminPassword(user, updatePasswordDTO.getNewPassword());
        StpUtil.logout();
    }

    private void validateParams(UpdatePasswordDTO updatePasswordDTO) {
        if (StrUtil.isBlank(updatePasswordDTO.getOldPassword())) {
            throw new FormValidationException("原密码不能为空");
        }
        if (StrUtil.isBlank(updatePasswordDTO.getNewPassword())) {
            throw new FormValidationException("新密码不能为空");
        }
        if (StrUtil.isBlank(updatePasswordDTO.getConfirmPassword())) {
            throw new FormValidationException("确认密码不能为空");
        }
    }

    private void validatePasswordConsistency(String newPwd, String confirmPwd) {
        if (!newPwd.equals(confirmPwd)) {
            throw new FormValidationException("新密码与确认密码不一致");
        }
        if (newPwd.length() < 6 || !newPwd.matches("^(?=.*[A-Za-z])(?=.*\\d).+$")) {
            throw new FormValidationException("密码需至少6位且包含字母数字");
        }
    }

    private void updateAdminPassword(User admin, String newPassword) {
        admin.setPassword(newPassword);
        updateById(admin);
    }
}
